/*
 * HackSQL
 * Copyright (C) 2008-2009
 *
 * Authors: Henrique Montenegro Decaria
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * $Id: hacksqld.c 50 2009-03-29 23:45:37Z felipensp $
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>

#include "hs_parser_common.h"
#include "hs_sql_parser.h"
#include <hacksql/hs_catalog.h>

#define HACKSQL_PORT    "4225"
#define HACKSQL_BACKLOG 20
#define HACKSQL_BUFFER  255

extern hs_catalog *catalog;

static int child_process(int child_sock)
{
	hs_parser_info parser_info;
	char buf[HACKSQL_BUFFER+1];
	int received;

	while (1) {		
		hs_query query;
		
		parser_info.socket = child_sock;
		parser_info.error = NULL;
		parser_info.query = &query;
		parser_info.query->num_select_itens = 0;
		parser_info.query->num_table_itens = 0;
		parser_info.query->all = 0;
		parser_info.query->distinct = 0;
		parser_info.query->dual = 0;
	
		received = recv(child_sock, buf, HACKSQL_BUFFER, 0);
		buf[received] = '\0';
	
		/*
		 * Here we pass buf to the parser. Note that buf might
		 * not contain the whole query. A logic needs to be added
		 * to determine when the client finished sending the query
		 * so we can pass it to the parser
		 */
	
		printf("Received data <%s> | their_sock is %d | received is %d\n", buf, child_sock, received);
		
		if (strcmp(buf, "close_hs_connection") == 0) {
			break;
		} else if (strncmp(buf, "USER", sizeof("USER")-1) == 0) {
			/* TODO: user authentication */
			printf("User authentication... %s\n", buf);
		} else {
			printf("Parsing query: <%s>\n", buf);

			if (hs_parser_query(&parser_info, buf) == 0) {
				send(child_sock, "Success", sizeof("Success"), 0);
			} else {
				char *error;
				int len;
				
				if (parser_info.error) {
					len = sizeof("Error: ") + strlen(parser_info.error);
				
					error = (char *)malloc(len);
					snprintf(error, len, "Error: %s", parser_info.error);
					
					send(child_sock, error, len, 0);
					
					free(error);
					free(parser_info.error);
				} else {
					send(child_sock, "Error: parse...", sizeof("Error: parse..."), 0);
				}
			}
		}
		
		if (received == 0) {
			break;
		}
	}
	
	/* Child process is finished with its work, so exit */
	close(child_sock);
	
	return 0;
}

int main(int argc, char **argv)
{
	int my_sock, their_sock;
	struct addrinfo *local;
	struct addrinfo info;
	struct sockaddr_storage remote;
	socklen_t remotelen;
	
	/*
		struct addrinfo {
			   int			  ai_flags;
			   int			  ai_family;
			   int			  ai_socktype;
			   int			  ai_protocol;
			   size_t		   ai_addrlen;
			   struct sockaddr *ai_addr;
			   char			*ai_canonname;
			   struct addrinfo *ai_next;
		};
	*/
	
	hs_catalog_init();
	
	memset(&info, 0, sizeof(info));
	info.ai_family = AF_INET; /* For now, only IPV4 */
	info.ai_socktype = SOCK_STREAM;
	info.ai_flags = AI_PASSIVE;
	
	if (getaddrinfo(NULL, HACKSQL_PORT, &info, &local) != 0) {
		printf("Error on getaddrinfo\n");
		return -1;
	}
	
	my_sock = socket(local->ai_family, local->ai_socktype, local->ai_protocol);
	
	if (my_sock < 0) {
		printf("Error creating the socket\n");
		return -1;
	}
	
	bind(my_sock, local->ai_addr, local->ai_addrlen);
	listen(my_sock, HACKSQL_BACKLOG);
	
	while (1) {
		remotelen = sizeof(remote);
		their_sock = accept(my_sock, (struct sockaddr *)&remote, &remotelen);
		
		if (their_sock == -1) {
			perror("Error accepting connection\n");
			continue;
		}
		
		if (!fork()) { /* Child process goes in */
			/* I am the child, daddy wont come here :) */
			close(my_sock);
			
			return child_process(their_sock);
		}
	}
	
	freeaddrinfo(local);
	
	close(my_sock);
	
	return 0;
}
